package com.t4.courseware.service.impl;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSException;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.t4.courseware.common.RedisKey;
import com.t4.courseware.common.Result;
import com.t4.courseware.config.AliyunConfig;
import com.t4.courseware.handler.UserThreadLocal;
import com.t4.courseware.mapper.UserMapper;
import com.t4.courseware.model.WXAuth;
import com.t4.courseware.model.WxUserInfo;
import com.t4.courseware.pojo.User;
import com.t4.courseware.pojo.dto.UserDto;
import com.t4.courseware.service.UserService;
import com.t4.courseware.service.WxService;
import com.t4.courseware.utils.FileUtils;
import com.t4.courseware.utils.JWTUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    // 允许上传图片的格式
    private static final String[] IMAGE_TYPE = {".bmp", ".jpg", ".jpeg", ".gif", ".png", ".gif"};

    @Resource
    private UserMapper userMapper;
    @Autowired
    private WxService wxService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Value("${wxmini.secret}")
    private String secret;
    @Value("${wxmini.appid}")
    private String appid;

    @Autowired
    private OSS ossClient;

    @Autowired
    private AliyunConfig aliyunConfig;


    public String getSessionId(String code) {
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code";
        url = url.replace("{0}", appid).replace("{1}", secret).replace("{2}", code);
        String res = HttpUtil.get(url);
        String uuid = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(RedisKey.WX_SESSION_ID + uuid, res, 30, TimeUnit.MINUTES);
        return uuid;
    }

    public UserDto login(UserDto userDto) {
        // 登录成功 封装用户信息到token
        userDto.setPassword(null);
        userDto.setUsername(null);
        userDto.setOpenId(null);
        userDto.setWxUnionId(null);
        String token = JWTUtils.sign(userDto.getId());
        userDto.setToken(token);
        //保存到redis内,下次就直接跳过验证
        redisTemplate.opsForValue().set(RedisKey.TOKEN_KEY + token, JSON.toJSONString(userDto), 7, TimeUnit.DAYS);
        return userDto;
    }

    public Result register(UserDto userDto) {
        User user = new User();
        BeanUtils.copyProperties(userDto,user);
        User queryUser = userMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getOpenId, user.getOpenId()));
        if (queryUser == null) {
            userMapper.insert(user);
        }
        //已存在直接登录
        return Result.SUCCESS(login(userDto));
    }

    public Result authLogin(WXAuth wxAuth) {
        try {
            String wxRes = wxService.wxDecrypt(wxAuth.getEncryptedData(), wxAuth.getSessionId(), wxAuth.getIv());
            WxUserInfo wxUserInfo = JSON.parseObject(wxRes,WxUserInfo.class);
            User user = userMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getOpenId, wxUserInfo.getOpenId()));
            wxUserInfo.setAvatarUrl(aliyunConfig.getUrlPrefix() + aliyunConfig.getDefaultAvatarPath());
            if (user != null) {
                //登录成功
                UserDto userDto = new UserDto();
                BeanUtils.copyProperties(user,userDto);
                return Result.SUCCESS(this.login(userDto));
            } else {
                UserDto userDto = new UserDto();
                userDto.from(wxUserInfo);
                return this.register(userDto);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.FAIL();
    }

    public Result userinfo(Boolean refresh) {
        UserDto userDto = UserThreadLocal.get();
        if (refresh){
            //刷新token
            String token = JWTUtils.sign(userDto.getId());
            userDto.setToken(token);
            redisTemplate.opsForValue().set(RedisKey.TOKEN_KEY + token,JSON.toJSONString(userDto),7,TimeUnit.DAYS);
        }
        return Result.SUCCESS(userDto);
    }

    @Override
    public Result updateUser(UserDto userDto) {
        User user = new User();
        BeanUtils.copyProperties(userDto, user);
        int res = userMapper.updateById(user);
        if(res != 1){
            return Result.FAIL();
        }
        UserDto finalUserDto = new UserDto();
        BeanUtils.copyProperties(user, finalUserDto);
        UserThreadLocal.put(finalUserDto);
        return Result.SUCCESS(finalUserDto);
    }

    @Override
    public Result uploadAvatar(MultipartFile multipartFile) {
        // 1. 图片校验，进行后缀名校验
        boolean isLegal = false;
        for(String type: IMAGE_TYPE){
            // 忽略大小写校验后缀名是否符合允许上传的格式
            if(StringUtils.endsWithIgnoreCase(multipartFile.getOriginalFilename(), type)){
                isLegal = true;
                break;
            }
        }
        // 不符合就设置文件上传状态为error
        if(!isLegal){
            return Result.FAIL();
        }

        // 2. 构建文件的新全路径名
        String ossFilePath = FileUtils.getOssFilePath(aliyunConfig.getAvatarRootDic(), multipartFile.getOriginalFilename());

        try{
            // 3. 上传文件到阿里云OSS
            ossClient.putObject(aliyunConfig.getBucketName(), ossFilePath, new ByteArrayInputStream(multipartFile.getBytes()));
        } catch (OSSException | ClientException | IOException e) {
            return Result.FAIL();
        }

        // 上传成功
        String url = aliyunConfig.getUrlPrefix() + ossFilePath;
        return Result.SUCCESS(url);
    }
}
